﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Remoting.Contexts;
using System.Text;
using System.Threading.Tasks;
using DGRAM_HEAD = JSIPDemo2.JSIP_HEAD_Para;
using SIGNATUREDATA = JSIPDemo2.jsSignature_Jsst_Data;
using UDP_RECV_CB_PARAM = JSIPDemo2.udp_recv_cb_Param;

namespace JSIPDemo2
{
    /// <summary>
    /// 测试JSSDK C++ dll
    /// </summary>
   public class JSSdkDemo
    {

        #region DLL 函数引用
        #region Init 初始化

        [DllImport("JsDzSdk.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern byte jsDZSDK_Init(bool DebugFlag, byte ProductType, byte deviceId, byte version);


        [DllImport("JsDzSdk.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern byte jsDZSDK_Init(bool DebugFlag, byte ProductType, byte deviceId, byte version, JsIP_KeyContactCallback keyContactCallback,
        JsIPJsst_callback GetJsst,
        JsIPMachineInfo_callback GetCpuid,
        JsIP_GetSignatureDataCallback GetSignature);
        #endregion

        #region 启动Socket

        /// <summary>
        /// 启动一个UDP客户端SOCKET，如果启动成功，则将该SOCKET添加到jsDZSDK通信上下文的链表当中
        /// </summary>
        /// <param name="socketId">输出参数，分配的SCOKET的标识ID，该ID唯一标识了一个SOCKET</param>
        /// <param name="port">要传送到的目的端口</param>
        /// <param name="udpClientCallback">该SocketId标识的SOCKET对应的接收回调函数</param>
        /// <returns>0x10：标识启动成功，其它值：启动失败</returns>
        [DllImport("JsDzSdk.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern byte jsDZSDK_StartUDPClient(ref ushort socketId, ushort port, JsIP_RecvThread_callback udpClientCallback);



        /// <summary>
        /// 启动一个UDP服务端SOCKET，如果启动成功，则将该SOCKET添加到jsDZSDK通信上下文的链表当中      
        /// </summary>
        /// <param name="socketId">输出参数，分配的SCOKET的标识ID，该ID唯一标识了一个SOCKET</param>
        /// <param name="udpServerCallback"></param>
        /// <param name="ip">IP层地址</param>
        /// <param name="port">输入参数,UDP通讯绑定的端口</param>
        /// <returns> 0x10：标识启动成功，其它值：启动失败</returns>
        [DllImport("JsDzSdk.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern byte jsDZSDK_StartUDPServer(ref ushort socketId, JsIP_RecvThread_callback udpServerCallback, string ip, ushort port);





        /// <summary>
        /// 启动一个TCP客户端SOCKET，如果启动成功，则将该SOCKET添加到jsDZSDK通信上下文的链表当中
        /// </summary>
        /// <param name="socketId">socketId: 输出参数，分配的SCOKET的标识ID，该ID唯一标识了一个SOCKET</param>
        /// <param name="tcpClientCallback">tcpClientCallback: 该SocketId标识的SOCKET对应的接收回调函数</param>
        /// <param name="ip">输入参数,要连接的Ip地址</param>
        /// <param name="port">输入参数，要连接的端口</param>
        /// <returns>0x10：标识启动成功，其它值：启动失败</returns>
        [DllImport("JsDzSdk.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern byte jsDZSDK_StartTCPClient(ref ushort socketId, JsIP_RecvThread_callback tcpClientCallback, string ip, ushort port);



        /// <summary>
        /// 启动一个TCP服务端SOCKET，如果启动成功，则将该SOCKET添加到jsDZSDK通信上下文的链表当中
        /// </summary>
        /// <param name="socketId">输出参数，分配的SCOKET的标识ID，该ID唯一标识了一个SOCKET</param>
        /// <param name="tcpClientCallback">该SocketId标识的SOCKET对应的接收回调函数</param>
        /// <param name="ip"></param>
        /// <param name="port">输入参数,TCP通讯服务端要侦听的端口值</param>
        /// <returns>0x10：标识启动成功，其它值：启动失败</returns>
        [DllImport("JsDzSdk.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern byte jsDZSDK_StartTCPServer(ref ushort socketId, JsIP_RecvThread_callback tcpClientCallback, string ip, ushort port);

        #endregion

        #region 关闭连接
        /// <summary>
        /// 关闭一个socket，包括TCP和UDP
        /// </summary>
        /// <param name="socketId">要关闭的socket</param>
        /// <returns>1:关闭成功，0:关闭失败</returns>
        [DllImport("JsDzSdk.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern byte jsDZSDK_Disconnect(int socketId);

        #endregion

        #region 发送数据

        [DllImport("JsDzSdk.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern ushort jsDZSDK_SendBySocketId(ushort socketId, ushort cmd, byte func_code, byte[] sendBuff, float timeout, ref int recvLen, byte[] recvBuff, ref int session_id);

        /// <summary>
        /// udp方式发送某个协议命令到相应的设备，设备执行完后回应相应数据到上位机
        /// </summary>
        /// <param name="socketId"></param>
        /// <param name="cmd">协议命令字</param>
        /// <param name="func_code">协议功能码</param>
        /// <param name="jsTag">自定义的标签，在捷顺以太网通讯协议规范中定义，对大部分功能都定义了标签</param>
        /// <param name="dataLength">要传输数据的数据长度</param>
        /// <param name="data">要传输的数据</param>
        /// <param name="ip">目的IP地址</param>
        /// <param name="port">目的端口</param>
        /// <param name="timeout">超时等待时长</param>
        /// <param name="recvLen">返回的数据长度</param>
        /// <param name="recvBuff">返回的数据内容</param>
        /// <param name="session_id">返回参数，会话ID</param>
        /// <returns>0x4F4B：标识命令执行成功，其它值标识执行失败。</returns>
        [DllImport("JsDzSdk.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern ushort jsDZSDK_UdpSynSendTo(ushort socketId, ushort cmd, byte func_code, ushort jsTag, uint dataLength, string data, string ip, ushort port, float timeout, ref int recvLen, byte[] recvBuff, ref int session_id);


        /// <summary>
        /// udp广播方式发送某个协议命令到相应的设备，设备执行后回应响应数据到上位机
        /// </summary>
        /// <param name="socketId"></param>
        /// <param name="cmd"></param>
        /// <param name="func_code"></param>
        /// <param name="sendBuff"></param>
        /// <param name="timeout"></param>
        /// <param name="recvLen">返回的数据长度</param>
        /// <param name="recvBuff">返回的数据内容</param>
        /// <param name="session_id">返回参数，会话ID</param>
        /// <returns>0x4F4B：标识命令执行成功，其它值标识执行失败。</returns>
        [DllImport("JsDzSdk.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern ushort jsDZSDK_BroadCast(ushort socketId, ushort cmd, byte func_code, string sendBuff, float timeout, ref int recvLen, ref byte[] recvBuff, ref int session_id);



        /// <summary>
        /// tcp方式发送某个协议命令到相应的设备，设备执行后回应响应数据到上位机
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        /// <param name="cmd"></param>
        /// <param name="func_code"></param>
        /// <param name="sendBuff"></param>
        /// <param name="timeout"></param>
        /// <param name="recvLen"></param>
        /// <param name="recvBuff"></param>
        /// <param name="session_id"></param>
        /// <param name="socketId"></param>
        /// <returns></returns>
        [DllImport("JsDzSdk.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern ushort jsDZSDK_SendByIPPORT(string ip, ushort port, ushort cmd, byte func_code, byte[] sendBuff, float timeout, ref int recvLen, out byte[] recvBuff, ref int session_id, ref ushort socketId);



        #endregion

        #region 退出

        /// <summary>
        /// 退出时的释放函数，断开所有的连接，并回收资源。
        /// </summary>
        /// <returns></returns>
        [DllImport("JsDzSdk.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern byte jsDZSDK_Exit();
        #endregion

        #region 同步时间
        /// <summary>
        ///将本机的时间与服务器的时间进行校对，本机时间同步为服务器时间
        /// </summary>
        /// <param name="ip">时间服务器的Ip地址</param>
        /// <param name="tzone">本地客户端所属时区所需要增加的时间差，北京：东八区，则参数值为正8, 范围[-12, +12],windows调用时不需关注</param>
        /// <returns>1:时间同步成功，0:时间同步失败</returns>
        [DllImport("JsDzSdk.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern bool jsDZSDK_SyncTimeByServer(string ip, int tzone);



        #endregion


        #region 其它

        /// <summary>
        /// 执行完一次命令后，手动调用该接口释放上下文相关内存
        /// 0x10:表示执行成功；其他值表示执行失败
        /// </summary>
        /// <returns></returns>
        [DllImport("JsDzSdk.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern int jsDZSDK_DeleteAPIContextManual(ushort socketId, ushort cmd, uint session_id);



        /// <summary>
        /// 注册连接回调函数
        /// </summary>
        /// <param name="cb"></param>
        [DllImport("JsDzSdk.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern void jsDZSDK_RegisterConnectionNotify(JsIP_ConnectionNotify cb);


        #endregion             

        #region 回调函数委托

        #region jsIPMachineInfo_callback
        /*
        函数指针名称：
	        jsIPMachineInfo_callback
        函数参数：
	        machineInfo为输出参数，标识到接收到的生产日期，操作员编号，CPUID等信息
        函数功能：
	        下位机读取machineInfo的函数指针定义，在JSIP库中调用，由下位机的调用方去实现
        返回值：
	        1:标识获取machineInfo成功，其它值标识失败
        */
        private delegate byte JsIPMachineInfo_callback(string machineInfo);
        private static JsIPMachineInfo_callback callback_jsIPMachineInfo = GetCpuid;
        private static byte GetCpuid(string machineInfo)
        {
            //TODO:处理返回结果
            return 0;
        }
        #endregion

        #region jsIP_RecvThread_callback
        private delegate void JsIP_RecvThread_callback(ushort socketId, ushort serverSockId, DGRAM_HEAD head, UInt16 len, ref string data, ref string srcIp, ref string srcPort);

        private static JsIP_RecvThread_callback callback_RecvThread = RecvThread;
        private static void RecvThread(ushort socketId, ushort serverSockId, DGRAM_HEAD head, UInt16 len, ref string data, ref string srcIp, ref string srcPort)
        {
            //TODO:处理返回结果
            data = null;
            srcIp = "";
            srcPort = "";
        }

        #endregion

        #region jsIP_RecvThread_udpcallback
        private delegate byte JsIP_RecvThread_udpcallback(UDP_RECV_CB_PARAM cbParam);
        private static JsIP_RecvThread_udpcallback callback_udpcallback = Udpcallback;
        private static byte Udpcallback(UDP_RECV_CB_PARAM cbParam)
        {
            //TODO:处理返回结果
            return 0;
        }
        #endregion

        #region jsIP_ConnectionNotify
        private delegate void JsIP_ConnectionNotify(int type, int status, ushort socketID, string destIP, int port);

        private static JsIP_ConnectionNotify JsIp_ConnectionNotify = ConnectionNotify;
        private static void ConnectionNotify(int type, int status, ushort socketID, string destIP, int port)
        {

        }
        #endregion

        #region JsIPJsst_callback
        /**         
        函数指针名称：
	        jsIPJsst_callback
        函数参数：
	        filename为输入参数，jsst文件名称，不包括后缀
	        JsstContent为输出参数，文件名称对应文件所包含的信息。
        函数功能：
	        在密钥认证时，由调用方去实现，在JSIP库中调用，调用后可获得jsst文件中的信息
        返回值：
	        1：标识函数执行成功，成功获取到信息，其它值标识失败
        */
        private delegate void JsIPJsst_callback(string filename, string JsstContent);
        private static JsIPJsst_callback callback_JsIPJsst = GetJsst;
        private static void GetJsst(string filename, string JsstContent)
        {
            Console.WriteLine($"JsIPJsst_callback:{filename}-{JsstContent}");

        }
        #endregion

        #region JsIP_KeyContactCallback
        private delegate JSIP_BOOL JsIP_KeyContactCallback(byte produttype);
        private static JsIP_KeyContactCallback callback_KeyContact = KeyContactCallback;
        private static JSIP_BOOL KeyContactCallback(byte produttype)
        {
            Console.WriteLine($"KeyContactCallback:{produttype}");
            return JSIP_BOOL.JSIP_FALSE;
        }
        #endregion

        #region JsIP_GetSignatureDataCallback
        private delegate JSIP_BOOL JsIP_GetSignatureDataCallback(SIGNATUREDATA jsstData);

        private static JsIP_GetSignatureDataCallback callback_GetSignatureData = GetSignature;

        private static JSIP_BOOL GetSignature(SIGNATUREDATA jsstData)
        {
            Console.WriteLine($"JsIP_GetSignatureDataCallback:{jsstData.ToString()}");
            return JSIP_BOOL.JSIP_FALSE;

        }
        #endregion

        #endregion
        #endregion


        #region 开放的公共函数
        /// <summary>
        ///  初始化JsDZSDK
        /// </summary>
        /// <param name="DebugFlag">JSIP_BOOL类型，JSIP_TRUE- 通信时，不采用加密的方式进行，并输出相关调试信息</param>
        /// <param name="ProductType">子系统产品类型，通过子系统产品类型可以区分哪些可以走PSAM卡的加密认证流程，哪些不能，采取随机数加密</param>
        /// <param name="deviceId">设备机号</param>
        /// <param name="version">JSIPSDK库的版本，按照协议约定的8个bit为标识，JSIPSDK库直接将版本在包头中进行传送</param>
        /// <returns>0x10:标识初始化JSDZSDK库的上下文环境成功，其它值标识初始化JSDZSDK库的上下文环境失败</returns>
        public static string InitJsDZSDK(bool DebugFlag, byte ProductType, byte deviceId, byte version)
        {
            //byte res = jsDZSDK_Init(DebugFlag, ProductType, deviceId, version);
            byte res = jsDZSDK_Init(DebugFlag, ProductType, deviceId, version,callback_KeyContact,callback_JsIPJsst,callback_jsIPMachineInfo,callback_GetSignatureData);
            if (res == 0x10)
                return "初始化成功";
            return "初始化失败";

        }

        public static byte Exit()
        {
            return jsDZSDK_Exit();

        }
        #endregion

    }




}
